#ifndef _M_HELPER_H_
#define _M_HELPER_H_

#include <sqlite3.h>
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <random>
#include <sstream>
#include <iomanip>
#include <atomic>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <cstring>
#include <cstdlib>
#include "logger.hpp"

// SQL操作工具
class SqliteHelper
{
    //保存位置, 一行数据的列数, 存储一行数据的字符指针数组 ,每一列字段名称
    using callback = int (*)(void *, int, char **, char **);

public:
    SqliteHelper(const std::string &filename) : m_filename(filename), m_handler(nullptr) {}

    // 打开数据库
    bool open(int safe_level = SQLITE_OPEN_FULLMUTEX)
    {
        // 判断文件是否存在
        std::ifstream f(m_filename.c_str());
        int ret = -1;

        if (!f.good())
        {
            LOG(logLevel::INFO) << "数据库文件尚未存在, 尝试新建并打开" << std::endl;
        }
        else
        {
            LOG(logLevel::INFO) << "数据库文件存在, 尝试打开" << std::endl;
        }

        ret = sqlite3_open_v2(m_filename.c_str(), &m_handler, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | safe_level, nullptr);

        if (ret == SQLITE_OK)
        {
            LOG(logLevel::INFO) << "数据库打开成功" << std::endl;
            return true;
        }
        else
        {
            LOG(logLevel::ERROR) << "数据库打开失败! 原因: " << sqlite3_errmsg(m_handler) << std::endl;
            return false;
        }
    }

    // 操作数据库
    bool exec(const std::string &sql, callback cb, void *buf)
    {
        char *errmsg;
        int ret = sqlite3_exec(m_handler, sql.c_str(), cb, buf, &errmsg);
        if (ret == SQLITE_OK)
        {
            LOG(logLevel::INFO) << "数据库操作成功" << std::endl;
            return true;
        }
        else
        {
            LOG(logLevel::ERROR) << "数据库操作失败! 原因: " << sqlite3_errmsg(m_handler) << std::endl;
            return false;
        }
    }

    // 关闭数据库
    void close()
    {
        sqlite3_close_v2(m_handler);
        LOG(logLevel::INFO) << "数据库关闭" << std::endl;
    }

private:
    const std::string &m_filename; // 文件路径
    sqlite3 *m_handler;            // 操作句柄
};

// 字符串切割工具
class StringHelper
{
public:
    static size_t split(const std::string &str, const std::string &sep, std::vector<std::string> *res)
    {
        if (str.empty() || sep.empty())
            return 0;
        size_t pos, idx = 0;
        while (idx < str.size())
        {
            pos = str.find(sep, idx);
            if (pos == std::string::npos)
            {
                res->push_back(str.substr(idx));
                break;
            }
            if (pos == idx)
            {
                idx += sep.size();
                continue;
            }
            res->push_back(str.substr(idx, pos - idx));
            idx = pos + sep.size();
        }
        return res->size();
    }
};

// UUID生存工具
class UuidHelper
{
public:
    static std::string uuid()
    {
        std::random_device rd;
        std::mt19937_64 gernator(rd());
        std::uniform_int_distribution<int> distribution(0, 255);
        std::stringstream ss;
        for (int i = 0; i < 8; i++)
        {
            ss << std::setw(2) << std::setfill('0') << std::hex << distribution(gernator);
            if (i == 3 || i == 5 || i == 7)
            {
                ss << "-";
            }
        }
        static std::atomic<size_t> seq(1);
        size_t num = seq.fetch_add(1);
        for (int i = 7; i >= 0; i--)
        {
            ss << std::setw(2) << std::setfill('0') << std::hex << ((num >> (i * 8)) & 0xff);
            if (i == 6)
                ss << "-";
        }
        return ss.str();
    }
};

// 文件操作工具
class FileHelper
{
public:
    FileHelper(const std::string &filename) : m_filename(filename) {}
    
    //查看文件是否存在
    bool exists()
    {
        struct stat st;
        return (stat(m_filename.c_str(), &st) == 0);
    }

    //获取文件大小
    size_t size()
    {
        struct stat st;
        int ret = stat(m_filename.c_str(), &st);
        if (ret < 0) 
        {
            return 0;
        }
        return st.st_size;
    }
    
    //读取文件数据
    bool read(char *body, size_t offset, size_t len)
    {
        // 1. 打开文件
        std::ifstream ifs(m_filename, std::ios::binary | std::ios::in);
        if (ifs.is_open() == false)
        {
            LOG(logLevel::ERROR) << "文件" << m_filename << "打开失败!";
            return false;
        }

        // 2. 跳转文件读写位置
        ifs.seekg(offset, std::ios::beg);

        // 3. 读取文件数据
        ifs.read(body,len);

        // 4. 判断读取操作是否成功
        if (ifs.good() == false)
        {
            LOG(logLevel::ERROR) << "读取操作失败!";
            return false;
        }

        // 5. 关闭文件
        ifs.close();
        return true;
    }

    //读取全部数据,这里需要通过body重设大小(输入), 并且输出数据到body(输出), 所以是输入输出型参数
    bool readAll(std::string &body)
    {
        // 获取文件大小，根据文件大小调整 body 的空间
        size_t fsize = this->size();
        body.resize(fsize);
        return read(&body[0], 0, fsize);
    }

    //写入数据
    bool write(const char* body, size_t offset, size_t len)
    {
        // 1. 打开文件
        std::fstream fs(m_filename, std::ios::binary | std::ios::in | std::ios::out);
        if (fs.is_open() == false)
        {
            LOG(logLevel::ERROR) << "文件" << m_filename << "打开失败!";
            return false;
        }

        // 2. 跳转到文件指定位置
        fs.seekp(offset, std::ios::beg);

        // 3. 写入数据
        fs.write(body, len);

        if (fs.good() == false)
        {
            LOG(logLevel::ERROR) << "写入操作失败!";
            return false;
        }

        // 4. 关闭文件
        fs.close();
        return true;
    }

    //写入全部数据
    bool writeAll(const std::string &body)
    {
        return write(&body[0], 0, body.size());
    }

    //重命名文件
    bool rename(const std::string &nname)
    {
        return ::rename(m_filename.c_str(), nname.c_str()) == 0;
    }

    //获取父目录
    static std::string parentDirectory(const std::string &filename)
    {

        size_t pos = filename.find_last_of("/");
        if (pos == std::string::npos)
        {
            return "./";
        }
        std::string path = filename.substr(0, pos);
        return path;
    }

    //创建文件
    static bool createFile(const std::string &filename)
    {
        std::fstream ofs(filename, std::ios::binary | std::ios::out);
        if (ofs.is_open() == false)
        {
            LOG(logLevel::ERROR) << "文件" << filename << "创建失败!";
            return false;
        }
        ofs.close();
        return true;
    }

    //删除文件
    static bool removeFile(const std::string &filename)
    {
        return ::remove(filename.c_str()) == 0;
    }

    //创建目录
    static bool createDirectory(const std::string &path)
    {
        size_t pos, idx = 0;
        while (idx < path.size())
        {
            pos = path.find("/", idx);
            if (pos == std::string::npos)
            {
                return mkdir(path.c_str(), 0775) == 0;
            }

            std::string subpath = path.substr(0, pos);
            int ret = mkdir(subpath.c_str(), 0775);

            if (ret != 0 && errno != EEXIST)
            {
                LOG(logLevel::ERROR) << "目录" << subpath.c_str() << "创建失败! 原因: " << strerror(errno);
                return false;
            }
            idx = pos + 1;
        }
        return true;
    }

    //删除目录
    static bool removeDirectory(const std::string &path)
    {
        std::string cmd = "rm -rf " + path;
        return system(cmd.c_str()) != -1;
    }

private:
    std::string m_filename;
};

#endif